iT邦幫忙

2026 iThome 鐵人賽

DAY 27
0
AI 自動化

AI 公司模擬器:Discord x Multi-Agent 架構實作系列 第 27 篇

DAY 27|讓 Bot 出錯後還能繼續用:錯誤處理與 Discord 實機修正

  • 分享至 

  • xImage
  •  

昨天整理了 Discord 的角色卡片、進度與操作提示。正常執行時,使用者可以看出目前輪到哪位 Agent,以及接下來要做什麼。

但流程中斷時,光顯示「發生錯誤」還不夠。模型逾時後要重新開始嗎?畫面沒出現結果,資料到底有沒有保存?按鈕送出失敗,再按一次會不會重複批准?

今天集中整理錯誤處理,讓 Bot 能說清楚保存狀態與接續方式。Discord 實測也發現三個問題:錯誤卡變成公開訊息、第二次查詢一直思考中,以及一筆舊資料讓整份專案清單失敗。這三項都已修正並重測通過。

錯誤卡要告訴使用者什麼?

這個專案會經過 Discord、Ollama 和 MySQL。錯誤可能發生在不同地方,處理方式也不同。

例如模型尚未下載,重送同一個 Prompt 沒有幫助;資料庫已提交卻沒收到回覆,直接重送建立操作又可能產生重複資料。

因此把分類放進 core/errors.py,用固定的 ErrorCode 對應原因、下一步與恢復策略。下面節錄模型缺失的設定:

ErrorCode.OLLAMA_MODEL_MISSING: (
    '設定的模型尚未可用。',
    '請管理員確認模型名稱與安裝,再重試原指令。',
    RetryPolicy.AFTER_FIX,
),

程式依例外型別、明確代碼與原始原因分類,不靠中文錯誤文字猜測。指令、自然語言入口與決策按鈕再交給共用的 ErrorHandler,產生一致的錯誤卡。

卡片包含「發生什麼」、「目前保存狀態」、「下一步」與「查詢代碼」。使用者可以先照提示操作;需要管理員協助時,再提供查詢代碼對照日誌。

底層例外不會直接貼到 Discord。即使模型回覆或資料庫錯誤包含額外內容,對外仍使用固定說明。

https://ithelp.ithome.com.tw/upload/images/20261011/20183880sR3BSgoa1a.png

模型出錯,不一定要重跑整場會議

Ollama 錯誤現在會區分連線失敗、逾時、模型缺失,以及回覆格式或內容沒有通過驗證。

會議管理器負責有限重試,預設最多兩次總嘗試,也就是第一次呼叫加一次重試。缺少模型這類必須先修正設定的錯誤,不會一直自動重跑。

成功且已保存的 Agent 步驟會保留在檢查點。討論中斷後,先用 /status 確認階段,再用 /resume 接續;草案或審查則依提示使用 /draft、/review。

這裡也分開「模型沒有完成」和「模型已完成,但顯示失敗」。如果結果已保存,只是 Discord 卡片沒有送出,程式會留下顯示失敗紀錄,不會把成功步驟改成失敗,也不會為了補畫面再呼叫一次模型。

流程結束或中斷時,仍要清除執行中的工作並釋放伺服器鎖。否則前一次出錯後,後續操作可能一直被判定為忙碌。

資料庫逾時,先確認有沒有寫進去

MySQL 的等待也加上期限:預設取得連線最多等 10 秒,SQL、讀取結果與交易操作各有 30 秒的等待上限。

但加上 timeout 並不代表逾時後就能安全重送。資料庫可能已完成 commit,只是回覆在傳回途中遺失。因此錯誤處理會保留交易進行到哪一步:

保存狀態 接下來的處理
尚未開始寫入 修正問題後再操作
已回滾 確認目前進度,再重試原操作
已提交 查詢已保存結果,不重做已完成操作
結果不明 先查證,不能直接宣稱沒有保存

唯讀查詢遇到暫時錯誤可以有限重試,寫入交易則不自動重播。批准方案原本的原子交易與版本檢查也保留。

自然語言建案另外加入 write_outcome_unknown 狀態。建立結果不明時,暫時擋住再次「確認」,也不能用「取消」清掉保護後重新建立。

使用者回覆「查證」,程式會查同一個伺服器、同一個實際生成的專案 ID。找到保存紀錄後才提供接續方式,不會拿相似標題猜是哪一筆。

這個保護狀態仍放在記憶體中,Bot 重啟後不會恢復未提交的收集對話。所以重啟後如果不確定先前是否建案成功,應先用 /projects 查看,不能直接假設草稿消失就代表資料沒寫入。

Discord 實測發現的三個問題

自動測試可以檢查程式邏輯,但替身物件不一定完整重現 Discord 的行為。今天的實測就找出三個正常流程測試沒有攔住的問題。

錯誤卡加了私人旗標,卻仍然公開

輸入不存在的專案 ID 執行 /start,Bot 正確辨識「找不到專案」,但錯誤卡出現在公開頻道。

原本程式先用公開 defer 告訴 Discord「正在處理」,出錯後才傳送 ephemeral=True 的 followup。ephemeral 代表只有操作的人看得到,卻不能在這個時點改變原等待訊息的私密狀態。

Discord 官方文件說明,緊接 deferred response 的第一則 followup 可能是完成原回覆,會保留初始 defer 的設定。官方 Followup 說明

現在 safe_error_reply() 會先確認原回覆是否仍是公開的等待訊息。如果是,就把它完成為中性通知,再另外傳送私人錯誤卡。公開通知只說操作未完成,沒有錯誤細節。

若原本已是私人等待訊息,就不額外增加公開通知;如果公開原回覆已完成,也不覆寫成功結果。私人錯誤傳送失敗時,則停止回覆,不改貼到公開頻道。

重測後,錯誤卡已顯示「只有您才能看到這個」,公開頻道只留下中性通知。這次沒有觀察到 Token 或密碼外洩,但原本的可見範圍仍然需要修正。

https://ithelp.ithome.com.tw/upload/images/20261011/20183880XSRRCifpW0.png

第二次 /status 一直顯示思考中

第一次查詢正常,第二次卻一直顯示「正在思考」。這次不是 Qwen 回得太慢,因為 /status 本來就不需要呼叫模型。

原因是程式找到上一次的狀態卡後,只更新舊訊息就結束,沒有完成這次新指令的回覆。每次 Slash 指令都有自己的 Interaction,編輯上一張卡片不等於回覆這一次操作。

現在移除跨 Interaction 的狀態卡快取,每次都完成自己的私人回覆。以下是產生頁面後的程式片段:

pages = build_status_embeds(
    record, is_running=running, event=active[1] if active else None
)
for page in pages:
    await safe_followup_send(interaction, embed=page, ephemeral=True)

連續兩次 /status 的重測都正常,第二次不再停在等待畫面,也沒有新增公開狀態卡。這裡調整的是私人查詢;DAY 26 用來追蹤長流程的頻道進度卡,仍可更新同一則訊息。

一筆舊資料,讓整份 /projects 清單失敗

第三個問題來自一筆缺少驗收條件的舊專案。轉成 Project 物件時,資料驗證失敗,連帶讓其餘正常專案也無法顯示。

錯誤又被歸成 INVALID_ARGUMENT,看起來像使用者把指令打錯,實際上需要修復的是已保存資料。

現在清單查詢會隔離異常紀錄,回傳有效專案與異常筆數。_build_projects() 的處理片段如下:

try:
    projects.append(Project.from_dict(data))
except ProjectDataError as error:
    if invalid_rows is not None:
        invalid_rows.append(project_id)
        continue
    raise ProjectRepositoryError(
        "MySQL 專案資料格式不正確。",
        code=ErrorCode.PROJECT_DATA_INVALID,
        write_outcome='not_started',
    ) from error

只有清單查詢允許略過異常資料。單筆讀取與啟動仍須通過完整驗證,而且啟動前就先檢查,避免不完整專案占用伺服器的進行中名額。

重測時,/projects 顯示 20 筆有效專案,另提示 1 筆資料待修復。那筆舊紀錄仍保留,沒有刪除,也沒有自行補造驗收條件。

https://ithelp.ithome.com.tw/upload/images/20261011/20183880Vaur1dRiSR.png

日誌留下查錯線索,不留下完整內容

錯誤集中處理後,也整理了終端機日誌。管理員需要知道哪個階段失敗,但不需要在每次例外時印出完整 Prompt、模型回答或 SQL 參數。

core/logging_security.py 記錄固定事件、錯誤代碼、查詢代碼、例外型別與必要識別資料。憑證遮罩則補在輸出端,處理已登記的秘密值,以及常見的 Bearer、password、token 等格式。

主要做法仍是少記錄內容。遮罩不能辨識所有自由文字,因此不能把「有打碼」當成可以隨意記錄需求與回答的理由。

修正後,再回到 Discord 確認

這些測試涵蓋模型失敗後接續、提交結果不明時禁止重送,以及錯誤回覆不洩漏原始內容。三個實機問題也都回到 Discord 重測,不能只看程式改完就算完成。

真實模型 qwen3.5:9b 的一般連線已確認能回覆;Ollama 與 MySQL 停服恢復、權限撤除、跨帳號及重啟後按鈕等故障情境,仍未全部完成實機驗收。

把中斷後的操作整理得更清楚,也補上這次實測找到的三個漏洞。之後再遇到錯誤,使用者至少能先確認保存狀態,知道該接續、查證,還是請管理員修正設定。


上一篇
# DAY 26|讓 Discord 裡的 Agent 討論更好讀:角色卡片、進度與操作提示
下一篇
DAY 28|把 AI 公司完整跑一遍:測試流程,也檢查方案內容
系列文
AI 公司模擬器:Discord x Multi-Agent 架構實作 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言